#ifndef _HOOK_SYSCALL_LOOKUP_ADDR_H
#define _HOOK_SYSCALL_LOOKUP_ADDR_H

#include <linux/kallsyms.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)

#include <linux/kprobes.h>

#define KPROBE_PRE_HANDLER(fname) \
    static int __kprobes fname(struct kprobe* p, struct pt_regs* regs)
static long unsigned int kln_addr = 0;
static struct kprobe     kp0, kp1;
unsigned long (*sh_kallsyms_lookup_name)(const char* name) = NULL;

KPROBE_PRE_HANDLER(handler_pre0)
{
#ifdef __x86_64__
    kln_addr = (--regs->ip);
#elif __aarch64__
    kln_addr = (--regs->pc);
#endif
    return 0;
}

KPROBE_PRE_HANDLER(handler_pre1) { return 0; }

static unsigned long syms_lookup_addr(const char* name)
{
    unsigned long addr = 0;
    kp0.symbol_name    = name;
    kp1.symbol_name    = name;
    kp0.pre_handler    = handler_pre0;
    kp1.pre_handler    = handler_pre1;

    if (register_kprobe(&kp0) < 0) return 0;
    if (register_kprobe(&kp1) < 0) {
        unregister_kprobe(&kp0);
        return 0;
    }

    addr = kln_addr;

    unregister_kprobe(&kp0);
    unregister_kprobe(&kp1);
    return addr;
}

#endif

unsigned long sh_lookup_addr(const char* name)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
    unsigned long addr = 0;
    addr               = syms_lookup_addr("kallsyms_lookup_name");
    if (addr == 0) return 0;
    sh_kallsyms_lookup_name = (unsigned long (*)(const char* name))addr;
    return sh_kallsyms_lookup_name(name);
#else
    return kallsyms_lookup_name(name);
#endif
}

#endif /* _HOOK_SYSCALL_LOOKUP_ADDR_H */
